using System;
using System.Xml;
using System.Collections.Specialized;

namespace stunnware.CRM.Fetch
{
    /// <summary>
    /// FetchEntityBase is the abstract base class of <see cref="FetchEntity"/> and <see cref="FetchLinkEntity"/>. It defines
    /// the properties used in both classes. 
    /// </summary>
    public abstract class FetchEntityBase
    {

        private string _entityName;
        private FetchColumnSet _columnSet;
        private readonly FetchOrderCollection _orders = new FetchOrderCollection();
        private FetchFilter _filter;
        private readonly FetchLinkEntityCollection _linkedEntities = new FetchLinkEntityCollection();

        /// <summary>
        /// 
        /// </summary>
        public FetchEntityBase() { }

        /// <summary>
        /// Creates a new instance bound to the specified entity.
        /// </summary>
        /// <param name="entityName">The entity name.</param>
        public FetchEntityBase(string entityName)
        {
            _entityName = entityName;

        }

        /// <summary>
        /// The entity name.
        /// </summary>
        public string EntityName
        {
            get
            {
                return _entityName;
            }
            set { _entityName = value; }
        }

        /// <summary>
        /// The attributes to return.
        /// </summary>
        public FetchColumnSet Columns
        {
            get
            {
                return _columnSet;
            }

            set
            {
                _columnSet = value;
            }
        }

        /// <summary>
        /// The sort order.
        /// </summary>
        public FetchOrderCollection Orders
        {
            get
            {
                return _orders;
            }
        }

        /// <summary>
        /// The filter.
        /// </summary>
        public FetchFilter Filter
        {
            get
            {
                return _filter;
            }

            set
            {
                _filter = value;
            }
        }

        /// <summary>
        /// A list of linked entities.
        /// </summary>
        public FetchLinkEntityCollection LinkEntities
        {
            get
            {
                return _linkedEntities;
            }
        }

        /// <summary>
        /// Used internally.
        /// </summary>
        /// <param name="parentElement"></param>
        internal void AddElements(XmlElement parentElement)
        {

            if (Columns != null)
            {
                Columns.AddToFetchDocument(parentElement);
            }

            if (Orders != null)
            {
                foreach (FetchOrder order in Orders)
                {
                    order.AddToFetchDocument(parentElement);
                }
            }

            if (Filter != null)
            {
                Filter.AddToFetchDocument(parentElement);
            }

            if (LinkEntities != null)
            {
                foreach (FetchLinkEntity linkEntity in LinkEntities)
                {
                    linkEntity.AddToFetchDocument(parentElement);
                }
            }
        }

        /// <summary>
        /// Used internally.
        /// </summary>
        /// <param name="node"></param>
        protected void PopulateFromXml(XmlNode node)
        {

            XmlNode allAttributesNode = node.SelectSingleNode("all-attributes");

            if (allAttributesNode != null)
            {
                this.Columns = new FetchColumnSet(true);
            }

            else
            {
                XmlNodeList attributeNodes = node.SelectNodes("attribute");
                StringCollection columnNames = new StringCollection();
                string alias = null;
                FetchAggregate aggregate = FetchAggregate.None;


                foreach (XmlNode attributeNode in attributeNodes)
                {
                    XmlAttribute nameAttribute = attributeNode.Attributes["name"];

                    if ((nameAttribute != null) && (nameAttribute.Value.Length > 0))
                    {
                        columnNames.Add(nameAttribute.Value);
                    }

                    XmlAttribute aggregateNode = attributeNode.Attributes["aggregate"];

                    if ((aggregateNode != null) && (aggregateNode.Value == "count"))
                    {
                        aggregate = FetchAggregate.Count;

                        XmlAttribute aliasNode = attributeNode.Attributes["alias"];

                        if ((aliasNode != null) && (aliasNode.Value.Length > 0))
                        {
                            alias = aliasNode.Value;
                        }
                    }
                }

                if (columnNames.Count > 0)
                {
                    string[] columns = new string[columnNames.Count];
                    columnNames.CopyTo(columns, 0);
                    this.Columns = new FetchColumnSet(columns);
                    this.Columns.Aggregate = aggregate;
                    this.Columns.Alias = alias;
                }
            }

            XmlNodeList orderNodes = node.SelectNodes("order");
            foreach (XmlNode orderNode in orderNodes)
            {
                this.Orders.Add(FetchOrder.FromXml(orderNode));
            }

            XmlNode filterNode = node.SelectSingleNode("filter");
            if (filterNode != null)
            {
                this.Filter = FetchFilter.FromXml(filterNode);
            }

            XmlNodeList linkNodes = node.SelectNodes("link-entity");
            foreach (XmlNode linkNode in linkNodes)
            {
                this.LinkEntities.Add(FetchLinkEntity.FromXml(linkNode));
            }


        }
    }
}